using System;
using System.Collections.Generic;
using System.Text;
using NHibernate.Criterion;

namespace MySHQ.DAL
{
    public class BaseManager<T>
    {
        private NHibernate.ISession _session;
        protected NHibernate.ISession Session { get { return _session; } set { _session = value; } }

        public BaseManager() { this.Session = SessionManager.GetCurrentSession(); }

        /// <summary>
        /// Retrieves the entity with the given id
        /// </summary>
        /// <param name="id"></param>
        /// <returns>the entity or null if it doesn't exist</returns>
        public T Get(object id)
        {
            return Session.Get<T>(id);
        }

        /// <summary>
        /// Saves or updates the given entity
        /// </summary>
        /// <param name="entity"></param>
        public void SaveOrUpdate(T entity)
        {
            Session.SaveOrUpdate(entity);
        }

        /// <summary>
        /// Returns each entity that matches the given criteria
        /// </summary>
        /// <param name="criteria"></param>
        /// <returns></returns>
        public IList<T> FindAll(DetachedCriteria criteria)
        {
            return criteria.GetExecutableCriteria(Session).List<T>();
        }

        /// <summary>
        /// Returns each entity that maches the given criteria, and orders the results 
        /// according to the given Orders
        /// </summary>
        /// <param name="criteria"></param>
        /// <param name="orders"></param>
        /// <returns></returns>
        public IList<T> FindAll(DetachedCriteria criteria, params Order[] orders)
        {
            if (orders != null)
            {
                foreach (Order order in orders)
                {
                    criteria.AddOrder(order);
                }
            }
            return FindAll(criteria);
        }

        /// <summary>
        /// Returns each entity that matches the given criteria, with support for paging, 
        /// and orders the results according to the given Orders
        /// </summary>
        /// <param name="criteria"></param>
        /// <param name="firstResult"></param>
        /// <param name="numberOfResults"></param>
        /// <param name="orders"></param>
        /// <returns></returns>
        public IList<T> FindAll(DetachedCriteria criteria, int firstResult, int numberOfResults, params Order[] orders)
        {
            criteria.SetFirstResult(firstResult).SetMaxResults(numberOfResults);
            return FindAll(criteria, orders);
        }

        /// <summary>
        /// Returns the one entity that matches the given criteria. Throws an exception if 
        /// more than one entity matches the criteria
        /// </summary>
        /// <param name="criteria"></param>
        /// <returns></returns>
        public T FindOne(DetachedCriteria criteria)
        {
            return criteria.GetExecutableCriteria(Session).UniqueResult<T>();
        }

        /// <summary>
        /// Returns the first entity to match the given criteria
        /// </summary>
        /// <param name="criteria"></param>
        /// <returns></returns>
        public T FindFirst(DetachedCriteria criteria)
        {
            IList<T> results = criteria.SetFirstResult(0).SetMaxResults(1)
                .GetExecutableCriteria(Session).List<T>();
            if (results.Count > 0)
            {
                return results[0];
            }
            return default(T);
        }

        /// <summary>
        /// Returns the first entity to match the given criteria, ordered by the given order
        /// </summary>
        /// <param name="criteria"></param>
        /// <param name="order"></param>
        /// <returns></returns>
        public T FindFirst(DetachedCriteria criteria, Order order)
        {
            return FindFirst(criteria.AddOrder(order));
        }

        /// <summary>
        /// Returns the total number of entities that match the given criteria
        /// </summary>
        /// <param name="criteria"></param>
        /// <returns></returns>
        public long Count(DetachedCriteria criteria)
        {
            return Convert.ToInt64(criteria.GetExecutableCriteria(Session)
                .SetProjection(Projections.RowCountInt64()).UniqueResult());
        }

        /// <summary>
        /// Returns true if at least one entity exists that matches the given criteria
        /// </summary>
        /// <param name="criteria"></param>
        /// <returns></returns>
        public bool Exists(DetachedCriteria criteria)
        {
            return Count(criteria) > 0;
        }

        /// <summary>
        /// Deletes the given entity
        /// </summary>
        /// <param name="entity"></param>
        public void Delete(T entity)
        {
            Session.Delete(entity);
        }


        /// <summary>
        /// Deletes every entity that matches the given criteria
        /// </summary>
        /// <param name="criteria"></param>
        public void Delete(DetachedCriteria criteria)
        {
            // a simple DELETE FROM ... WHERE ... would be much better, but i haven't found 
            // a way to do this yet with Criteria. So now it does two roundtrips... one for
            // the query, and one with all the batched delete statements (that is, if you've 
            // enabled CUD statement batching 
            foreach (T entity in FindAll(criteria))
            {
                Delete(entity);
            }
        }


    }
}
